home *** CD-ROM | disk | FTP | other *** search
Text File | 1987-03-03 | 29.3 KB | 1,044 lines |
- ;;
- ;; This is file: MACROS.MLB.
- ;;
- ;; It contains the source for a macro library.
- ;; Written by Mark Hersey.
- ;;
- ;; Comments by Lew Paper, 2/16/87
- ;;
- ;; Print macro expansion control by Lew Paper
- ;; Default is .XALL, which prints code only
- ;; Change by macro variable MACRO_EXPANSION_CONTROL
- ;; 0 for .XALL, which prints code only
- ;; 1 for .SALL, which surpresses all printing
- ;; 2 for .LALL, which prints both code and comments
- .XCREF SET_MACRO_EXPANSION
- SET_MACRO_EXPANSION MACRO
- IFNDEF MACRO_EXPANSION_CONTROL
- .XALL
- ENDIF ;; IFNDEF MACRO_EXPANSION_CONTROL
- IFDEF MACRO_EXPANSION_CONTROL
- IF MACRO_EXPANSION_CONTROL EQ 1
- .SALL
- ELSE
- IF MACRO_EXPANSION_CONTROL EQ 2
- .LALL
- ENDIF ;; IF MACRO_EXPANSION_CONTROL EQ 2
- ENDIF ;; IF MACRO_EXPANSION_CONTROL EQ 1
- ENDIF ;; IFDEF MACRO_EXPANSION_CONTROL
- ENDM ;; SET_MACRO_EXPANDSION
- ;;
- ;; I8086 symbols repeated by LP
- IFNDEF TRUE
- TRUE EQU 001H ;Used to indicate true condition.
- ENDIF ;IFNDEF TRUE
- IFNDEF ZFLAG
- ZFLAG EQU 00040H
- ENDIF ;IFNDEF ZFLAG
- ;;
- ;; Everything from here to the end of macro POP_REGISTERS builds
- ;; the macro pair PUSH_REGISTERS and POP_REGISTERS. PUSH_REGISTERS
- ;; pushes a list of registers and builds a stack for POP_REGISTERS
- ;; to restore them from.
- ;; LP
- ;;
- ;; Convert a REGISTER to a SERIAL position in LIST
- ;; Set SERIAL to 0 if no match
- .XCREF REGISTER_TO_SERIAL_FOR_LIST
- REGISTER_TO_SERIAL_FOR_LIST MACRO REGISTER, SERIAL, LIST
- .XCREF SERIAL_LIST_ITEM
- SERIAL_LIST_ITEM = 0 ;; Initialize
- SERIAL = 0 ;; Assume no match
- IRP REGISTER_LIST_ITEM, <LIST>
- SERIAL_LIST_ITEM = SERIAL_LIST_ITEM + 1
- ;; Serial number for this list item
- IFIDN <REGISTER>, <REGISTER_LIST_ITEM>
- SERIAL = SERIAL_LIST_ITEM
- EXITM
- ENDIF ;; IFIDN
- ENDM ;; IRP REGISTER_LIST_ITEM, <Upper case>
- ENDM ;; REGISTER_TO_SERIAL_FOR_LIST
- ;;
- ;; Convert a REGISTER to a SERIAL position in the standard register list
- ;; Set SERIAL to 0 if no match
- .XCREF REGISTER_TO_SERIAL
- REGISTER_TO_SERIAL MACRO REGISTER, SERIAL
- REGISTER_TO_SERIAL_FOR_LIST REGISTER, SERIAL, <AX,BX,CX,DX,SI,DI,SP,BP,DS,ES,SS>
- IFE SERIAL ;; No upper case match
- REGISTER_TO_SERIAL_FOR_LIST REGISTER, SERIAL, <ax,bx,cx,dx,si,di,sp,bp,ds,es,ss>
- ENDIF ;; IFE SERIAL
- ENDM ;; REGISTER_TO_SERIAL
- ;;
- ;; POP a register which matches SERIAL in the standard register list
- ;; If unable to convert, enter
- ;; "ERROR, BAD SPEC FOR SERIAL_TO_POP_REGISTER" as a syntax error
- .XCREF SERIAL_TO_POP_REGISTER
- SERIAL_TO_POP_REGISTER MACRO SERIAL
- .XCREF UNMATCHED_SERIAL_TO_POP_REGISTER, SERIAL_LIST_ITEM
- SERIAL_LIST_ITEM = 0 ;; Initialize
- UNMATCHED_SERIAL_TO_POP_REGISTER = 1
- ;; Assume no match
- IRP REGISTER_LIST_ITEM, <AX,BX,CX,DX,SI,DI,SP,BP,DS,ES,SS>
- SERIAL_LIST_ITEM = SERIAL_LIST_ITEM + 1
- IF SERIAL EQ SERIAL_LIST_ITEM
- POP REGISTER_LIST_ITEM
- UNMATCHED_SERIAL_TO_POP_REGISTER = 0
- ;; Show match
- EXITM
- ENDIF ;; IF SERIAL EQ SERIAL_LIST_ITEM
- ENDM ;; IRP REGISTER_LIST_ITEM, <...>
- IF UNMATCHED_SERIAL_TO_POP_REGISTER
- ERROR, BAD SPEC FOR SERIAL_TO_POP_REGISTER
- ENDIF ;; IF UNMATCHED_SERIAL_TO_POP_REGISTER
- ENDM ;; SERIAL_TO_POP_REGISTER
- ;;
- .XCREF PHASE_1_REGISTER_STACK_INIT, PHASE_2_REGISTER_STACK_INIT
- PHASE_1_REGISTER_STACK_INIT = 1
- PHASE_2_REGISTER_STACK_INIT = 1
- ;;
- ;; Initial the register stack if necessary
- .XCREF REGISTER_STACK_INIT_IF_NECESSARY
- REGISTER_STACK_INIT_IF_NECESSARY MACRO
- .XCREF REGISTER_STACK_TOP
- IF1
- IF PHASE_1_REGISTER_STACK_INIT ;; Not initialized
- REGISTER_STACK_TOP = 0
- PHASE_1_REGISTER_STACK_INIT = 0
- ENDIF ;; IF PHASE_1_REGISTER_STACK_INIT
- ENDIF ;; IF1
- IF2
- IF PHASE_2_REGISTER_STACK_INIT ;; Not re-initialized
- REGISTER_STACK_TOP = 0
- PHASE_2_REGISTER_STACK_INIT = 0
- ENDIF ;; IF PHASE_2_REGISTER_STACK_INIT
- ENDIF ;; IF2
- ENDM ;; REGISTER_STACK_INIT_IF_NECESSARY
- ;;
- ;; Save VALUE in REGISTER_STACK(RS_TOP). RS_TOP is always
- ;; REGISTER_STACK_TOP, so this is really the end of PUSH_REGISTER_STACK
- ;; which is necessary to create the name of REGISTER_STACK_TOP, because
- ;; MASM only evaluates arguments.
- .XCREF SAVE_REGISTER_STACK
- SAVE_REGISTER_STACK MACRO RS_TOP, VALUE
- .XCREF REGISTER_STACK&RS_TOP
- REGISTER_STACK&RS_TOP = VALUE
- ENDM ;; SAVE_REGISTER_STACK
- ;;
- ;; Push REGISTER_STACK_VALUE on the register stack
- ;; Initialize the register stack if necessary.
- .XCREF PUSH_REGISTER_STACK
- PUSH_REGISTER_STACK MACRO REGISTER_STACK_VALUE
- REGISTER_STACK_INIT_IF_NECESSARY
- REGISTER_STACK_TOP = REGISTER_STACK_TOP + 1
- SAVE_REGISTER_STACK %REGISTER_STACK_TOP, REGISTER_STACK_VALUE
- ENDM ;; PUSH_REGISTER_STACK
- ;;
- ;; PUSH REGISTR
- ;; Push the serial register index for REGISTR on the register
- ;; stack and return it in REGISTER_SERIAL.
- ;; If an error, enter
- ;; "ERROR, UNABLE TO PUSH REGISTER " and the register name as a
- ;; syntax error and set REGISTER_SERIAL to 0.
- .XCREF PUSH_REGISTER_SERIAL
- PUSH_REGISTER_SERIAL MACRO REGISTR, REGISTER_SERIAL
- REGISTER_TO_SERIAL REGISTR, REGISTER_SERIAL
- ;; Find serial for standard register
- ;; list
- IF REGISTER_SERIAL ;; No error
- PUSH REGISTR
- PUSH_REGISTER_STACK %REGISTER_SERIAL
- ELSE ;; Error
- ERROR, UNABLE TO PUSH REGISTER REGISTR
- ;; Error message as syntax error
- ENDIF ;; IF REGISTER_SERIAL
- ENDM ;; PUSH_REGISTER_SERIAL
- ;;
- ;; POP the register whose serial number is in
- ;; REGISTER_STACK(RS_TOP). RS_TOP is always REGISTER_STACK_TOP, so
- ;; this is really an intermediate step of POP_REGISTER_STACK, which
- ;; is necessary to create the name of the variable and to get its
- ;; value, because MASM only evaluate arguments.
- .XCREF REGISTER_STACK_TO_POP_REGISTER
- REGISTER_STACK_TO_POP_REGISTER MACRO RS_TOP
- SERIAL_TO_POP_REGISTER %(REGISTER_STACK&RS_TOP)
- ENDM ;; REGISTER_STACK_TO_POP_REGISTER
- ;;
- ;; POP the register whose serial number is at the top of the
- ;; register stack.
- ;; Pop the register stack
- ;; If the register stack is empty, enter
- ;; "ERROR, UNABLE TO POP THE EMPTY REGISTER STACK" as a syntax error
- .XCREF POP_REGISTER_STACK
- POP_REGISTER_STACK MACRO
- IF REGISTER_STACK_TOP ;; Not empty
- REGISTER_STACK_TO_POP_REGISTER %REGISTER_STACK_TOP
- REGISTER_STACK_TOP = REGISTER_STACK_TOP - 1
- ;; Pop the register stack
- ELSE ;; Empty
- ERROR, UNABLE TO POP THE EMPTY REGISTER STACK
- ENDIF ;; IF REGISTER_STACK_TOP
- ENDM ;; POP_REGISTER_STACK
- ;;
- .XCREF PUSH_REGISTERS,POP_REGISTERS
- ;;
- ;; Push the contents of a list of registers on the program's stack.
- ;; Also set up a register stack in the macro language so that
- ;; POP_REGISTERS will pop them in the proper reversed order.
- PUSH_REGISTERS MACRO REGS
- ;; REGS is the list of registers. The < and > are requred.
- ;; Separate the registers with commas.
- .XCREF REGISTER_STACK_COUNT, REGISTER_SERIAL
- REGISTER_STACK_COUNT = 0 ;; Initialize. Will contain a
- ;; count of the registers pushed.
- ;; At the end, will be the top of
- ;; the register stack
- IRP REG,<REGS>
- PUSH_REGISTER_SERIAL REG, REGISTER_SERIAL
- ;; PUSH register REG on the program's
- ;; stack. Push the serial number for
- ;; its name on the register stack
- IF REGISTER_SERIAL ;; No errors
- REGISTER_STACK_COUNT = REGISTER_STACK_COUNT + 1
- ENDIF ;; IF REGISTER_SERIAL
- ENDM ;; IRP REG,<REGS>
- PUSH_REGISTER_STACK %REGISTER_STACK_COUNT
- ENDM ;; PUSH_REGISTERS
- ;;
- ;; Pop the register stack to REGISTER_STACK_COUNT. RS_TOP is
- ;; always REGISTER_STACK_TOP. It is necessary because MASM only
- ;; evaluates arguments
- .XCREF POP_REGISTER_STACK_COUNT
- POP_REGISTER_STACK_COUNT MACRO RS_TOP
- REGISTER_STACK_COUNT = REGISTER_STACK&RS_TOP
- REGISTER_STACK_TOP = REGISTER_STACK_TOP - 1
- ENDM ;; POP_REGISTER_STACK_COUNT
- ;;
- ;; Pop the program's stack to the registers which PUSH_REGISTERS saved
- ;; If there is nothing saved by PUSH_REGISTERS, enter
- ;; "ERROR, NO REGISTERS LEFT TO POP" as a syntax error.
- POP_REGISTERS MACRO
- REGISTER_STACK_INIT_IF_NECESSARY
- IF REGISTER_STACK_TOP ;; PUSH_REGISTERS has been run
- ;; and not cleared
- POP_REGISTER_STACK_COUNT %REGISTER_STACK_TOP
- ;; Pop the register stack to
- ;; REGISTER_STACK_COUNT
- IF REGISTER_STACK_COUNT ;; Some registers saved
- REPT REGISTER_STACK_COUNT
- POP_REGISTER_STACK ;; POP the proper register and pop
- ;; the register stack
- ENDM ;; REPT REGISTER_STACK_COUNT
- ENDIF ;; IF REGISTER_STACK_COUNT
- ELSE ;; PUSH_REGISTERS has not been run or has been
- ;; cleared
- ERROR, NO REGISTERS LEFT TO POP
- ENDIF ;; IF REGISTER_STACK_TOP
- ENDM ;; POP_REGISTERS
- ;;
- ;;
- ;; This ends the PUSH_REGISTERS - POP_REGISTERS material. LP
- ;;
- .XCREF PUSHM,POPM
- ;;
- PUSHM MACRO REGS
- IRP REG,<REGS>
- PUSH REG
- ENDM
- ENDM
- ;;
- POPM MACRO REGS
- IRP REG,<REGS>
- POP REG
- ENDM
- ENDM
- ;;
- .XCREF GETZ,GETNZ,NOTZ
- ;;
- ;; Set AL to TRUE if the ZERO flag is set. Set it to FALSE
- ;; if the ZERO flag is reset. Don't change the ZERO flag.
- GETZ MACRO
- LOCAL SKIPZ
- MOV AL,TRUE
- JZ SKIPZ
- XOR AL,AL
- SKIPZ:
- AND AL,AL
- ENDM
- ;; Set AL to TRUE if the ZERO flag is reset. Set it to FALSE
- ;; if the ZERO flag is set. Don't change the ZERO flag.
- ;;
- GETNZ MACRO
- LOCAL SKIPNZ
- MOV AL,TRUE
- JNZ SKIPNZ
- XOR AL,AL
- SKIPNZ:
- ENDM
- ;;
- ;; Reverse the ZERO flag. As a side effect, set AH to 40H if
- ;; the zero flag was set and to 0 if it was not.
- NOTZ MACRO
- LAHF
- AND AH,ZFLAG
- ENDM
- ;;
- .XCREF JNNC,JNNZ,JNNNZ,JNNE,JNNNE,JNNS,JU,JNU,JNNCXZ
- ;; All of the JNN? macros remove the "NN" which other macro
- ;; substitutions can insert. JU is an unconditional jump, and JNU
- ;; is a NOP, which is the negation of an unconditional jump.
- JNNC MACRO LABEL
- JC LABEL
- ENDM
- ;;
- JNNZ MACRO LABEL
- JZ LABEL
- ENDM
- ;;
- JNNNZ MACRO LABEL
- JNZ LABEL
- ENDM
- ;;
- JNNE MACRO LABEL
- JE LABEL
- ENDM
- ;;
- JNNNE MACRO LABEL
- JNE LABEL
- ENDM
- ;;
- JNNS MACRO LABEL
- JS LABEL
- ENDM
- ;;
- JU MACRO LABEL
- JMP SHORT LABEL
- ENDM
- ;;
- JNU MACRO LABEL ;No code: "Jump never"!
- ENDM
- ;;
- JNNCXZ MACRO LABEL
- JCXZ LABEL
- ENDM
- ;;
- .XCREF JNNB,JNNA
- JNNB MACRO LABEL
- JB LABEL
- ENDM
- ;;
- JNNA MACRO LABEL
- JA LABEL
- ENDM
- ;;
- ;;
- ;; Additional macros to eliminate the effects of JNN? by Lew
- ;; Paper
- .XCREF JNNBE, JNNLE, JNNL, JNNGE, JNNG, JNNO, JNNP, JNPO, JNPE
- ;;
- JNNBE MACRO LABEL
- JBE LABEL
- ENDM ;; JNNBE
- ;;
- JNNLE MACRO LABEL
- JLE LABEL
- ENDM ;; JNNLE
- ;;
- JNNL MACRO LABEL
- JL LABEL
- ENDM ;; JNNL
- ;;
- JNNGE MACRO LABEL
- JGE LABEL
- ENDM ;; JNNGE
- ;;
- JNNG MACRO LABEL
- JG LABEL
- ENDM ;; JNNG
- JNNO MACRO LABEL
- JO LABEL
- ENDM ;; JNNO
- ;;
- JNNP MACRO LABEL
- JP LABEL
- ENDM ;; JNNP
- ;;
- JNPO MACRO LABEL
- JPE LABEL
- ENDM ;; JNPO
- ;;
- JNPE MACRO LABEL
- JPO LABEL
- ENDM ;; JNPE
- ;;
- ;;
- ;; The macros in this section are by Lew Paper to automate
- ;; the choices between LONG and SHORT backward jumps and to comment
- ;; unncessary LONG forward jumps.
- ;;
- ;;
- ;; Sets SHORT_LABEL to 1 if BACK_LABEL is within 126 bytes of $
- ;; or to 0 if it is move than 126 bytes away. Does not check that
- ;; BACK_LABEL precedes $.
- .XCREF SET_SHORT_BACK
- SET_SHORT_BACK MACRO BACK_LABEL
- .XCREF SHORT_LABEL
- SHORT_LABEL = $ - OFFSET BACK_LABEL LE 126
- ENDM ;; SET_SHORT_BACK
- ;;
- ;; Sets FORWARD_SHORT_LABEL to 1 if FORWARD_LABEL is within
- ;; MAX_BYTES bytes of $ or to 0 if it is move than MAX_BYTES bytes away.
- ;; MAX_BYTES is 127 bytes maximum for all SHORT jumps plus 3 for the
- ;; size of a LONG jump which is added to a short jump around it for
- ;; conditional jumps. Does not check that FORWARD_LABEL follows $.
- .XCREF SET_SHORT_FORWARD
- SET_SHORT_FORWARD MACRO FORWARD_LABEL, MAX_BYTES
- .XCREF FORWARD_SHORT_LABEL
- IF1 ;; Must define macro variables in
- ;; pass 1
- FORWARD_SHORT_LABEL = 0 ;; Value is insignificant
- ENDIF ;; IF1
- IF2 ;; FOWARD_LABEL is only defined on
- ;; pass 2
- FORWARD_SHORT_LABEL = OFFSET FORWARD_LABEL - $ LE MAX_BYTES
- ENDIF ;; IF2
- ENDM ;; SET_SHORT_FORWARD
- ;;
- IFDEF LP_DEBUG
- .XCREF D_SHORT_LABEL
- D_SHORT_LABEL MACRO
- .LALL
- IF SHORT_LABEL
- ; SHORT_LABEL is true
- ELSE
- ; SHORT_LABEL is false
- ENDIF ;; IF SHORT_LABEL
- SET_MACRO_EXPANSION
- ENDM ;; D_SHORT_LABEL
- ENDIF ;; IFDEF LP_DEBUG
- ;;
- ;; Print a comment that a LONG jump to FORWARD_LABEL is not
- ;; necessary if it isn't. Set UNNECESSARY_LONG_COMMENTS to 1. See
- ;; SET_SHORT_FORWARD for a description of MAX_BYTES.
- .XCREF IS_LONG_NECESSARY
- IS_LONG_NECESSARY MACRO FORWARD_LABEL, MAX_BYTES
- .XCREF UNNECESSARY_LONG_COMMENTS
- SET_SHORT_FORWARD FORWARD_LABEL, MAX_BYTES
- ;; Set foward_short_label to TRUE
- ;; if LONG is not necessary
- IF1 ;; Macro variables must be defined
- ;; in pass 1
- UNNECESSARY_LONG_COMMENTS = 0
- ;; Value doesn't matter
- ENDIF ;; IF1
- IF FORWARD_SHORT_LABEL ;; Can only be TRUE on pass 2
- .LALL ;; List this comment
- ;****** This jump doesn't need to be LONG ******
- .SALL
- SET_MACRO_EXPANSION
- UNNECESSARY_LONG_COMMENTS = 1
- ENDIF ;; IF SHORT_LABEL
- ENDM ;; IS_LONG_NECESSARY
- ;;
- ;; Write a warning of unncessary long comments
- .XCREF OUT_UNNECESSARY_LONG_COMMENTS
- OUT_UNNECESSARY_LONG_COMMENTS MACRO
- IFDEF UNNECESSARY_LONG_COMMENTS
- IF UNNECESSARY_LONG_COMMENTS
- ;; Can only be positive on phase 2
- .LALL
- ;
- ; There were some LONG jumps which could have been SHORT.
- ;
- .SALL
- %OUT *
- %OUT * There were some LONG jumps which could have been SHORT.
- %OUT *
- ENDIF ;; IF UNNECESSARY_LONG_COMMENTS
- ENDIF ;; IFDEF UNNECESSARY_LONG_COMMENTS
- ENDM ;; OUT_UNNECESSARY_LONG_COMMENTS
- ;;
- ;; Set BACKWARD_JUMP to 1 if a jump to TARGET_LABEL is
- ;; backward or 0 if it is forward.
- .XCREF SET_BACKWARD_JUMP
- SET_BACKWARD_JUMP MACRO TARGET_LABEL
- .XCREF BACKWARD_JUMP
- IF1
- IFNDEF TARGET_LABEL ;; Must be forward jump
- BACKWARD_JUMP = 0
- ENDIF ;; IFNDEF TARGET_LABEL
- IFDEF TARGET_LABEL ;; Must be backward jump
- BACKWARD_JUMP = 1
- ENDIF ;; IFDEF TARGET_LABEL
- ENDIF ;; IF1
- IF2
- BACKWARD_JUMP = $ - OFFSET TARGET_LABEL GT 0
- ENDIF ;; IF2
- ENDM ;; SET_BACKWARD_JUMP
- ;;
- ;; Unconditional jump to TARGET_LABEL. Automatically
- ;; determine whether backward jump is LONG or SHORT. Set some
- ;; value in LONG to force a forward long jump. If a forward
- ;; jump is LONG but could be SHORT, write a remark to that effect
- ;; and set UNNECESSARY_LONG_COMMENTS to 1.
- .XCREF LJMP
- LJMP MACRO TARGET_LABEL, LONG
- LOCAL BYPASS
- SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
- IF BACKWARD_JUMP
- SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
- ELSE ;; Forward
- .XCREF SHORT_LABEL
- SHORT_LABEL = 1 ;; Assume SHORT
- IFNB <LONG> ;; LONG is not blank
- .XCREF LJMP_MAX_SHORT
- LJMP_MAX_SHORT = 127 + OFFSET BYPASS - $
- IS_LONG_NECESSARY TARGET_LABEL, LJMP_MAX_SHORT
- ;; Write comment for unnecessary LONG
- SHORT_LABEL = 0
- ENDIF ;; IFNB
- ENDIF ;; IF BACKWARD_JUMP
- IF SHORT_LABEL
- JMP SHORT TARGET_LABEL
- ELSE ;; LONG
- JMP TARGET_LABEL
- ENDIF ;; If short label
- IFE BACKWARD_JUMP ;; Forward jump
- .SALL
- .XCREF BYPASS
- BYPASS:
- SET_MACRO_EXPANSION
- ENDIF ;; IFE BACKWARD_JUMP
- ENDM ;; LJMP
- ;;
- ;; Conditional jump to TARGET_LABEL. Automatically
- ;; determine whether backward jump is LONG or SHORT. Set some
- ;; value in LONG to force a forward long jump. If a forward
- ;; jump is LONG but could be SHORT, write a remark to that effect
- ;; and set UNNECESSARY_LONG_COMMENTS to 1.
- ;; CONDITION is any completion to the J conditional jump prefix
- ;; except CXZ, which deson't have a negation.
- .XCREF LCJMP
- LCJMP MACRO CONDITION, TARGET_LABEL, LONG
- LOCAL BYPASS
- SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
- IF BACKWARD_JUMP
- SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
- ELSE ;; Forward
- SHORT_LABEL = 1 ;; Assume SHORT
- IFNB <LONG> ;; LONG is not blank
- .XCREF LCJMP_MAX_SHORT
- LCJMP_MAX_SHORT = 127 + OFFSET BYPASS - $
- IS_LONG_NECESSARY TARGET_LABEL, LCJMP_MAX_SHORT
- ;; Write comment for unnecessary LONG
- SHORT_LABEL = 0
- ENDIF ;; IFNB
- ENDIF ;; IF BACKWARD_JUMP
- IF SHORT_LABEL
- J&CONDITION TARGET_LABEL
- ELSE ;; LONG
- JN&CONDITION BYPASS
- JMP TARGET_LABEL
- ENDIF ;; If short label
- IFE SHORT_LABEL AND BACKWARD_JUMP
- ;; Everything but short backward jump
- IF SHORT_LABEL ;; Short forward jump
- .XCREF BYPASS
- .SALL
- ENDIF ;; IF SHORT_LABEL
- BYPASS:
- IF SHORT_LABEL ;; Short forward jump
- SET_MACRO_EXPANSION
- ENDIF ;; IF SHORT_LABEL
- ENDIF ;; IFE SHORT_LABEL AND BACKWARD_JUMP
- ENDM ;; LCJMP
- ;;
- ;; JCXZ to target label. Automatically determine whether
- ;; backward jump is LONG or SHORT. Set some value in LONG to force
- ;; a forward long jump. If a forward jump is LONG but could be
- ;; SHORT, write a remark to that effect and set
- ;; UNNECESSARY_LONG_COMMENTS to 1.
- .XCREF LJCXZ
- LJCXZ MACRO TARGET_LABEL, LONG
- LOCAL BYPASS
- SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
- IF BACKWARD_JUMP
- SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
- ELSE ;; Forward
- SHORT_LABEL = 1 ;; Assume SHORT
- IFNB <LONG> ;; LONG is not blank
- .XCREF LJXCZ_MAX_SHORT
- LJXCZ_MAX_SHORT = 127 + OFFSET BYPASS - $
- IS_LONG_NECESSARY TARGET_LABEL, LJXCZ_MAX_SHORT
- ;; Write comment for unnecessary LONG
- SHORT_LABEL = 0
- ENDIF ;; IFNB
- ENDIF ;; IF BACKWARD_JUMP
- IF SHORT_LABEL
- JCXZ TARGET_LABEL
- ELSE ;; LONG
- OR CX,CX ;; Is CX zero?
- JNZ BYPASS ;; No. Bypass long jump
- JMP TARGET_LABEL
- ENDIF ;; If short label
- IFE SHORT_LABEL AND BACKWARD_JUMP
- ;; Everything but short backward jump
- IF SHORT_LABEL ;; Short forward jump
- .XCREF BYPASS
- .SALL
- ENDIF ;; IF SHORT_LABEL
- BYPASS:
- IF SHORT_LABEL ;; Short forward jump
- SET_MACRO_EXPANSION
- ENDIF ;; IF SHORT_LABEL
- ENDIF ;; IFE SHORT_LABEL AND BACKWARD_JUMP
- ENDM ;; LJCXZ
- ;;
- ;; Loop to TARGET_LABEL. Automatically determine whether a
- ;; backward jump is LONG or SHORT. Set some value in LONG to force
- ;; a forward long jump. If a forward jump is LONG but could be SHORT,
- ;; write a remark to that effect and set UNNECESSARY_LONG_COMMENTS to 1.
- ;; CONDITION is blank for LOOP, E for LOOPE, Z for LOOPZ,
- ;; NZ for LOOPNZ or NE for LOOPNE.
- .XCREF LLOOP
- LLOOP MACRO CONDITION, TARGET_LABEL, LONG
- LOCAL BYPASS
- SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
- IF BACKWARD_JUMP
- SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
- ELSE ;; Forward
- SHORT_LABEL = 1 ;; Assume SHORT
- IFNB <LONG> ;; LONG is not blank
- .XCREF LLOOP_MAX_SHORT
- LLOOP_MAX_SHORT EQU 127 + OFFSET BYPASS - $
- IS_LONG_NECESSARY TARGET_LABEL, LLOOP_MAX_SHORT
- ;; Write comment for unnecessary LONG
- SHORT_LABEL = 0
- ENDIF ;; IFNB
- ENDIF ;; IF BACKWARD_JUMP
- IF SHORT_LABEL
- LOOP&CONDITION TARGET_LABEL
- ELSE ;; LONG
- IFNB <CONDITION> ;; Don't test unconditional loop
- JN&CONDITION BYPASS ;; Terminate loop if not CONDITION
- ENDIF ;; IFNB CONDITION
- DEC CX ;; Does CX become zero
- JZ BYPASS ;; Yes. Terminate loop
- JMP TARGET_LABEL
- ENDIF ;; If short label
- IFE SHORT_LABEL AND BACKWARD_JUMP
- ;; Everything but short backward jump
- IF SHORT_LABEL ;; Short forward jump
- .XCREF BYPASS
- .SALL
- ENDIF ;; IF SHORT_LABEL
- BYPASS:
- IF SHORT_LABEL ;; Short forward jump
- SET_MACRO_EXPANSION
- ENDIF ;; IF SHORT_LABEL
- ENDIF ;; IFE SHORT_LABEL AND BACKWARD_JUMP
- ENDM ;; LLOOP
- ;;
- ;;
- ;; From here on, the macros are more complicated. They
- ;; use automatic labels, whose names are sequentially indexed as
- ;; L?1, L?2, ... Macro variable L?CNT contains the last index used,
- ;; and the next label will be L?&(L?CNT+1). They also use a macro
- ;; stack, whose members are indexec as S?1, S?2, ... Each Sn contains a
- ;; number which the macros use to construct a L?n label. Macro
- ;; variable S?CNT contains the S? index of the top of the macro stack,
- ;; so a push will increment S?CNT, and a pop will decrement it.
- ;;
- ;; Mark Hersey named the macro variables consistently as
- ;; follows:
- ;; ENTRY The index of a macro stack variable.
- ;; NUMBER The index of a label or a macro stack
- ;; variable.
- ;; CONDITION The condition part of a jump instruction,
- ;; i.e., JZ would have CONDITION Z.
- ;; LONG This is always an optional variable. If
- ;; it exists, it forces a LONG jump of some
- ;; sort. If not, it forces a SHORT jump.
- ;; VAR1 The required first variable of a compare.
- ;; VAR2 The optional second variable of a compare.
- ;; If it exists, it forces a CMP instruction.
- ;; If it doesn't, the macro uses an
- ;; AND VAR1,VAR1 instruction, which lets you
- ;; test for zero or a negative variable.
-
- ;; To simplify the descriptions below, I will use the following
- ;; terminology:
- ;; L?N is a label with index N.
- ;; CURRENT is L?CNT.
- ;; NEXT is L?CNT+1.
- ;; SECOND is L?CNT+2.
- ;; S?N is the contents of macro stack variable with index N.
- ;; stacktop is the contents of S?(S?CNT), the top of the stack.
- ;; stacknext is (stacktop + 1).
- ;;
- ;; Add 1 to contents of LABEL. Written by LP, with
- ;; modifications to later macros which had this operation.
- ;;
- ;; Initialize the label and macro stack indices to 0.
- .XCREF XINIT
- XINIT MACRO
- .XCREF L?CNT,S?CNT
- L?CNT = 0
- S?CNT = 0
- ENDM
- ;;
- ;; Initialize the label and macro stack indices to 0 if necessary
- .XCREF PHASE_1_XINIT, PHASE_2_XINIT, XINIT_IF_NECESSARY
- PHASE_1_XINIT = 1
- PHASE_2_XINIT = 1
- XINIT_IF_NECESSARY MACRO
- IF1
- IF PHASE_1_XINIT ;; Not initialized
- XINIT
- PHASE_1_XINIT = 0
- ENDIF ;; IF PHASE_1_XINIT
- ENDIF ;; IF1
- IF2
- IF PHASE_2_XINIT ;; Not re-initialized
- XINIT
- PHASE_2_XINIT = 0
- ENDIF ;; IF PHASE_2_XINIT
- ENDIF ;; IF2
- ENDM ;; XINIT_IF_NECESSARY
- ;;
- .XCREF XINC
- XINC MACRO LABEL
- XINIT_IF_NECESSARY
- LABEL = LABEL+1
- ENDM ;; XINC
- ;;
- .XCREF XSAVE,XPUSH,XPOP
- ;;
- ;; Set macro stack variable with index ENTRY to label index
- ;; NUMBER. In these macros, ENTRY is always S?CNT, and NUMBER is
- ;; always L?CNT, so the macro becomes, "Set stacktop to the current
- ;; label index."
- XSAVE MACRO ENTRY,NUMBER
- .XCREF S?&ENTRY
- S?&ENTRY= NUMBER
- ENDM
- ;;
- ;; Push the next label index on the macro stack.
- ;; Recall that XINC invokes XINIT_IF_NECESSARY.
- XPUSH MACRO
- XINC L?CNT ;; Set CURRENT to NEXT
- XINC S?CNT ;; Set stacktop to stacknext
- XSAVE %S?CNT,%L?CNT
- ENDM
- ;;
- ;; Pop the macro stack.
- ;; No automatic initialization to force an error if no XPUSH first.
- XPOP MACRO
- S?CNT = S?CNT-1
- ENDM
- ;;
- .XCREF XLBL,XLBLS,XLBLI
- ;;
- XLBL MACRO NUMBER
- .XCREF L?&NUMBER
- L?&NUMBER:
- ENDM
- ;;
- ;; Write the label L?(S?NUMBER):. In these macros, NUMBER is
- ;; always S?CNT, so the macro becomes, "Write the label L?stacktop."
- XLBLS MACRO NUMBER
- XLBL %S?&NUMBER
- ENDM
- ;;
- ;; Write the label L?(stacktop+1).
- XLBLI MACRO NUMBER
- XINIT_IF_NECESSARY
- XLBL %(S?&NUMBER&+1)
- ENDM
- ;;
- .XCREF XJMP,XJMPS,XJMPI
- ;;
- ;; Jump to L?NUMBER.
- XJMP MACRO NUMBER,LONG
- LJMP L?&NUMBER, LONG
- ENDM
- ;;
- ;; Jump to L?(S?NUMBER).
- XJMPS MACRO NUMBER,LONG
- XJMP %S?&NUMBER,LONG
- ENDM
- ;;
- ;; Jump to L?(S?NUMBER+1).
- XJMPI MACRO NUMBER,LONG
- XJMP %(S?&NUMBER+1),LONG
- ENDM
- ;;
- .XCREF XLP,XLPS
- ;;
- ;; LOOP for CONDITION to L?NUMBER.
- XLP MACRO CONDITION,NUMBER,LONG
- LLOOP CONDITION,L?&NUMBER,LONG
- ENDM
- ;;
- ;; LOOP for CONDITION to L?(S?NUMBER). In these macros,
- ;; S?NUMBER is always S?CNT, so the macro becomes, "LOOP for CONDITION
- ;; to L?stacktop."
- XLPS MACRO CONDITION,NUMBER,LONG
- XLP CONDITION,%S?&NUMBER,LONG
- ENDM
- ;;
- .XCREF XCJMP,XCJMPS,XCJMPI,XCXZJMP
- ;;
- ;; Jump on condition to L?NUMBER.
- XCJMP MACRO CONDITION,NUMBER,LONG
- LCJMP CONDITION,L?&NUMBER,LONG
- ENDM
- ;;
- ;; Jump on CONDITION to L?(S?NUMBER).
- XCJMPS MACRO CONDITION,NUMBER,LONG
- XCJMP CONDITION,%S?&NUMBER,LONG
- ENDM
- ;;
- ;; Jump on CONDITION to L?(S?NUMBER+1).
- XCJMPI MACRO CONDITION,NUMBER,LONG
- XINIT_IF_NECESSARY
- XCJMP CONDITION,%(S?&NUMBER+1),LONG
- ENDM
- ;;
- ;; Jump if CX = 0 to label L?NUMBER.
- XCXZJMP MACRO NUMBER,LONG
- LJCXZ L?&NUMBER,LONG
- ENDM
- ;;
- .XCREF XIFC,XANDIFC,COMPARE,XIF,XANDIF,XELSE,XENDIF
- ;;
- ;; Push the next label index on the macro stack.
- ;; Jump if CONDITION is false to L?stacktop.
- ;; NOTE that the jump is apparently backward to allow for ANDing
- ;; conditions with XANDIFC.
- XIFC MACRO CONDITION,LONG
- XPUSH
- XCJMP N&CONDITION,%L?CNT,LONG
- ENDM
- ;;
- ;; Jump if CONDITION is false to L?stacktop.
- ;; Use only after invoking XIFC to set up the macro stack.
- XANDIFC MACRO CONDITION,LONG
- XCJMP N&CONDITION,%L?CNT,LONG
- ENDM
- ;;
- ;; Compare VAL1 to either itself or VAL2.
- COMPARE MACRO VAL1,VAL2
- IFNB <VAL2>
- CMP VAL1,VAL2
- ENDIF
- IFB <VAL2>
- AND VAL1,VAL1
- ENDIF
- ENDM
- ;;
- ;; Compare VAL1 to either itself or VAL2.
- ;; Jump if CONDITION is false to L?stacktop.
- ;; NOTE that the jump is apparently backward to allow for ANDing
- ;; conditions with XANDIFC.
- XIF MACRO VAL1,CONDITION,VAL2,LONG
- COMPARE <VAL1>,<VAL2>
- XIFC CONDITION,LONG
- ENDM
- ;;
- ;; Compare VAL1 to either itself or VAL2.
- ;; Jump if CONDITION is false to L?stacktop.
- ;; Use only after invoking XIFC to set up the macro stack.
- XANDIF MACRO VAL1,CONDITION,VAL2,LONG
- COMPARE <VAL1>,<VAL2>
- XANDIFC CONDITION,LONG
- ENDM
- ;;
- ;; Increment CURRENT and jump to L?CURRENT.
- ;; Write a l?stacktop here as a label
- ;; Replace stacktop with CURRENT.
- XELSE MACRO LONG
- XINC L?CNT ;; Set CURRENT to NEXT
- XJMP %L?CNT,LONG
- XLBLS %S?CNT
- XSAVE %S?CNT,%L?CNT
- ENDM
- ;;
- ;; Write L?stacktop here as a label.
- ;; Pop the macro stack.
- XENDIF MACRO
- XLBLS %S?CNT
- XPOP
- ENDM
- ;;
- .XCREF XLOOP,XENDLP,XBEGIN,XEND,XNEXT,XCNEXTC,XCNEXT,XCEXITC,XEXIT
- .XCREF XCEXIT,XWHILEC,XWHILE,XREPEAT,XUNTILC,XUNTIL,XFOR,XDEC
- ;;
- ;; Increment CURRENT and push it on the macro stack
- ;; Write L?CURRENT here as a label
- ;; Increment CURRENT again, so that it is the original SECOND
- XLOOP MACRO
- XPUSH
- XLBL %L?CNT
- XINC L?CNT ;; Set CURRENT to NEXT
- ENDM
- ;;
- ;; Increment CURRENT and push it on the macro stack
- ;; Write L?CURRENT as a label.
- XBEGIN MACRO
- XPUSH
- XLBL %L?CNT
- XINC L?CNT ;; Set CURRENT to NEXT
- ENDM
- ;;
- ;; Jump on CONDITION to stacktop
- XNEXT MACRO LONG
- XJMPS %S?CNT,LONG
- ENDM
- ;;
- ;; Jump on CONDITION to stacktop
- XCNEXTC MACRO CONDITION,LONG
- XCJMPS CONDITION,%S?CNT,LONG
- ENDM
- ;;
- ;; Compare VAL1 to VAL2 or itself
- ;; Jump on CONDITION to stacktop
- XCNEXT MACRO VAL1,CONDITION,VAL2,LONG
- COMPARE <VAL1>,<VAL2>
- XCNEXTC CONDITION,LONG
- ENDM
- ;;
- ;; Jump to stacknext.
- XEXIT MACRO LONG
- XJMPI %S?CNT,LONG
- ENDM
- ;;
- ;; Jump on condition to stacknext.
- XCEXITC MACRO CONDITION,LONG
- XCJMPI CONDITION,%S?CNT,LONG
- ENDM
- ;;
- ;; Compare VAL1 with either itself or VAL2
- ;; Jump on condition to stacknext
- XCEXIT MACRO VAL1,CONDITION,VAL2,LONG
- COMPARE <VAL1>,<VAL2>
- XCEXITC CONDITION,LONG
- ENDM
- ;;
- ;; Jump to stacktop
- ;; Write the label L?stacknext
- XENDLP MACRO LONG
- XJMPS %S?CNT,LONG
- XLBLI %S?CNT
- XPOP
- ENDM
- ;;
- ;; Write the label L?stacknext
- ;; Pop the macro stack
- XEND MACRO
- XLBLI %S?CNT
- XPOP
- ENDM
- ;;
- ;; Increment CURRENT and push it on the macro stack
- ;; Write L?CURRENT here as a label
- ;; Increment CURRENT again, so that it is the original SECOND
- ;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
- XWHILEC MACRO CONDITION,LONG
- XLOOP
- XCEXITC N&CONDITION,LONG
- ENDM
- ;;
- ;; Increment CURRENT and push it on the stack
- ;; Write L?CURRENT here as a label
- ;; Increment CURRENT again, so that it is the original SECOND
- ;; Compare VAL1 with either itself or VAL2
- ;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
- XWHILE MACRO VAL1,CONDITION,VAL2,LONG
- XLOOP
- XCEXIT <VAL1>,N&CONDITION,<VAL2>,LONG
- ENDM
- ;;
- ;; Increment CURRENT and push it on the stack
- ;; Write L?CURRENT here as a label
- ;; Increment CURRENT again, so that it is the original SECOND
- XREPEAT MACRO
- XLOOP
- ENDM
- ;;
- ;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
- ;; Write L?NEXT here as a label
- ;; Pop the macro stack
- XUNTILC MACRO CONDITION
- XCJMPS N&CONDITION,%S?CNT
- XLBLI %S?CNT
- XPOP
- ENDM
- ;;
- ;; Compare VAL1 with either itself or VAL2
- ;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
- ;; Write L?NEXT here as a label
- ;; Pop the macro stack
- XUNTIL MACRO VAL1,CONDITION,VAL2
- COMPARE <VAL1>,<VAL2>
- XUNTILC CONDITION
- ENDM
- ;;
- ;; If no arguments, forward jump to L?SECOND
- ;; Increment L?CNT and push it
- ;; Write L?CURRENT here as a label
- ;; Increment L?CURREMT again, so it is now the original SECOND
- XFOR MACRO NOZERO,LONG
- ;; NOZERO's value doesn't matter. Any value will surpress the forward
- ;; jump.
- IFB <NOZERO>
- XINIT_IF_NECESSARY
- XCXZJMP %(L?CNT+2),LONG
- ENDIF
- XLOOP
- ENDM
- ;;
- ;; LOOP on CONDITION to L?stacktop
- ;; Write the label L?(stacktop+1).
- ;; POP the macro stack
- XDEC MACRO CONDITION,LONG
- XLPS CONDITION,%S?CNT,LONG
- XLBLI %S?CNT
- XPOP
- ENDM
- ;;
- .XCREF CALLSTR,FCBCALL,GETCALL
- ;;
- ;; In the next three macros,
- ;; ROUTINE is the routine name.
- ;; STRING is the name of the variable which BX or DX is to point to.
- ;;
- ;; Call a routine which uses an offset in DX
- CALLSTR MACRO ROUTINE,STRING
- MOV DX,OFFSET STRING
- CALL ROUTINE
- ENDM
- ;;
- ;; Call a routine which uses an offset in BX
- GETCALL MACRO ROUTINE,STRING
- MOV BX,OFFSET STRING
- CALL ROUTINE
- ENDM
- ;;
- ;; Call a routine which calls a DOS file control block function.
- ;; The routine should reset the zero flag if it succeeds and set
- ;; the zero flag for failure.
- ;;
- FCBCALL MACRO ROUTINE,FCB,ERRORRTN
- ;; ERRORRTN is an optional label for an error routine.
- MOV DX,OFFSET FCB
- CALL ROUTINE
- IFNB <ERRORRTN>
- JZ ERRORRTN
- ENDIF
- ENDM
- ;;
- .CREF
- SET_MACRO_EXPANSION
- ;;
-